import threading
import const

from planner import Pred

class World:
	"""The world view of an agent."""
	lock = threading.Lock()

	def __init__(self, stacks):
		self.stacks = stacks

	def __str__(self):
		ret = ""
		for s in self.stacks:
			ret += str(s) + "\n"
		return ret

	def getBlocks(self):
		l = map(lambda x : x.blocks, self.stacks)
		return [item for sublist in l for item in sublist]

	def getBlock(self, block_id):
		for stack in self.stacks:
			for b in stack.blocks:
				if b.id == block_id:
					return b
		return None

	def pickUpBlock(self, block_id):
		"""Picks up the given block. Returns False if the block is not clear."""
		blockStack = None
		for stack in self.stacks:
			if stack.blocks[-1].id == block_id:
				blockStack = stack 
		if blockStack is None:
			return None
		block = blockStack.blocks[-1]
		blockStack.blocks.remove(block) 
		if not blockStack.blocks:
			self.stacks.remove(blockStack)
		return block

	def putOnBlock(self, block_id_1, block_id_2):
		"""Removes the first block from its location and places it on top of
		the second block. Returns False if one of the blocks is not clear."""
		destStack = None
		for stack in self.stacks:
			if stack.blocks[-1].id == block_id_2:
				destStack = stack 
		if destStack is None:
			return False 
		block1 = self.pickUpBlock(block_id_1)
		if block1 is None:
			return False 
		destStack.blocks.append(block1)
		return True
	
	def putOnGround(self, block_id):
		"""Picks up the given block and places it on the ground. Returns false
		if the block is not clear."""
		block = self.pickUpBlock(block_id)
		if block is None:
			return False
		pos = self.stacks[-1].max_width() + self.stacks[-1].startpos
		self.stacks.append(BlockStack(pos, [block]))
		# sort columns by position: uncomment when append is changed to insert
		# self.stacks.sort()
		return True

	def findMatchingBlocks(self, slot):
		"""Finds unowned blocks that match the slot"""
		filter_f = lambda block: slot.fits(block) and not block.isOwned()
		matches = filter(filter_f, self.getBlocks())
		return matches

	def findAllMatchingBlocks(self, slot):
		"""Finds both owned and unowned blocks that match the slot"""
		filter_f = lambda block: slot.fits(block)
		matches = filter(filter_f, self.getBlocks())
		return matches

	def setOwner(self, block_id, owner):
		"""Sets the owner of a block."""
		ret = False 
		self.lock.acquire()
		for block in self.getBlocks():
			if block.id == block_id and block.owner == None:
					block.setOwner(owner)
					ret = True
		self.lock.release()
		return ret

	def changeOwner(self, block_id, old_owner, new_owner):
		"""Changes the owner of a block"""
		ret = False
		self.lock.acquire()
		for block in self.getBlocks():
			if block.id == block_id and block.owner == old_owner:
					block.setOwner(new_owner)
					ret = True
		self.lock.release()
		return ret

	def worldState(self):
		preds = []
		for stack in self.stacks:
			preds.append(Pred(const.ONGROUND, stack.blocks[0].id))
			prev = stack.blocks[0].id
			for b in stack.blocks[1:]:
				preds.append(Pred(const.ON, b.id, prev))
				prev = b.id
			preds.append(Pred(const.CLEAR, stack.blocks[-1].id))
		return preds

class BlockStack:
	"""A stack of blocks."""

	def __init__(self, startpos, blocks):
		self.startpos = startpos
		self.blocks = blocks

	def __str__(self):
		ret = '[' 
		for b in self.blocks:
			ret += str(b) + ", "
		if ret[-1] == ' ':
			ret = ret[0:-2]
		return ret + ']'

	def __lt__(self, other):
		return self.startpos < other.startpos
	
	def max_width(self):
		return max(map(lambda x: x.props[const.WIDTH], self.blocks))
